package tools

import (
	"archive/zip"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"unsafe"

	log "github.com/sirupsen/logrus"
	"qabx.net/common/utils/math"
)

func Unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer func() {
		if err := r.Close(); err != nil {
			panic(err)
		}
	}()

	os.MkdirAll(dest, 0755)

	// Closure to address file descriptors issue with all the deferred .Close() methods
	extractAndWriteFile := func(f *zip.File) error {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer func() {
			if err := rc.Close(); err != nil {
				panic(err)
			}
		}()

		path := filepath.Join(dest, f.Name)

		if f.FileInfo().IsDir() {
			os.MkdirAll(path, f.Mode())
		} else {
			os.MkdirAll(filepath.Dir(path), f.Mode())
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer func() {
				if err := f.Close(); err != nil {
					panic(err)
				}
			}()

			_, err = io.Copy(f, rc)
			if err != nil {
				return err
			}
		}
		return nil
	}

	for _, f := range r.File {
		err := extractAndWriteFile(f)
		if err != nil {
			return err
		}
	}

	return nil
}

func GetAllFileByFilter(pathname string, filter string, group int) []int64 {
	var files []int64
	rd, err := ioutil.ReadDir(pathname)
	if err != nil {
		return files
	}
	for _, fi := range rd {
		if fi.IsDir() {
			continue
		} else {
			filename := path.Base(fi.Name())
			r, err := regexp.Compile(filter)
			if err != nil {
				continue
			}

			matchR := r.FindAllSubmatch([]byte(filename), -1)
			if len(matchR) == 0 {
				continue
			}

			if len(matchR[0]) == 0 || len(matchR[0]) < group {
				continue
			}

			var fileNameInt int64
			if fileNameIntTmp, err := strconv.ParseInt(string(matchR[0][group]), 10, 64); err != nil {
				log.Warnf("%s name is not an int,skip", string(matchR[0][group]))
				continue
			} else {
				fileNameInt = fileNameIntTmp
			}

			files = append(files, fileNameInt)
		}
	}

	math.QuickSort(files, 0, len(files)-1)
	return files
}

func String2Bytes(s string) []byte {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := reflect.SliceHeader{
		Data: sh.Data,
		Len:  sh.Len,
		Cap:  sh.Len,
	}
	return *(*[]byte)(unsafe.Pointer(&bh))
}

func Bytes2String(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}
